/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package tablemodel;

import Entities.Especial;
import Entities.MenuEjecutivo;
import Entities.Producto;
import ejb.Condicional;
import ejb.ConsultaUniversalFacadeLocal;
import ejb.soportes.Consulta;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;

/**
 *
 * Clase generica para los lazymodel del todo el proyecto. Utilizada para traer
 * los listados de forma que no se traiga toda la informacion de una desde el
 * servidor sino a medida que se va necesitando en el paginado.
 */
public class GeneralLazyModel<T> extends LazyDataModel<T> {

    ConsultaUniversalFacadeLocal consultaUniversalFacade = lookupConsultaUniversalFacadeLocal();
    private T newObject;
    private List<Condicional> condicionales;

    public GeneralLazyModel(T newObject) {
        this.newObject = newObject;
    }

    public List<Condicional> getCondicionales() {
        return condicionales;
    }

    public void setCondicionales(List<Condicional> condicionales) {
        this.condicionales = condicionales;
    }

    /**
     * Recibe la key de la fila de la tabla devuelve un objeto.
     *
     * @param rowKey
     * @return
     */
    @Override
    public T getRowData(String rowKey) {
        T resp = null;
        try {
            T q = this.newObject;
            Method m = q.getClass().getMethod("setId", new Class[]{int.class});
            m.invoke(q, new Object[]{Integer.valueOf(rowKey)});
            Consulta consulta = new Consulta(q);
            List<T> data = consultaUniversalFacade.consultar(consulta, new int[]{0, 1});
            if (data != null && data.size() > 0) {
                resp = data.get(0);
            }
        } catch (Exception e) {
        }
        return resp;
    }

//    @Override
//    public void setRowIndex(int rowIndex) {
//        if (rowIndex == -1 || getPageSize() == 0) {
//            super.setRowIndex(-1);
//        } else {
//            super.setRowIndex(rowIndex % getPageSize());
//        }
//    }
    @Override
    public Object getRowKey(T entidad) {
        Object resp = entidad;
        try {
            Method m = entidad.getClass().getMethod("getId", new Class[]{});
            resp = m.invoke(entidad, new Object[]{});

        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        }
        return resp;
    }

    /**
     * Funcion que carga los datos del modelo, recibe la primer pagina, el
     * tamaño de la misma, el filtro de orden, y los demas filtos.Devuelve una
     * lista de objetos.
     *
     * @param first
     * @param pageSize
     * @param sortField
     * @param sortOrder
     * @param filters
     * @return
     */
    @Override
    public List<T> load(int first, int pageSize, String sortField, SortOrder sortOrder, Map<String, String> filters) {
        List<T> data = new ArrayList<>();
        T q = this.newObject;
        List<Condicional> tempCondicionales = new ArrayList<>();
        boolean safe_enum = true;
        try {
            q = (T) this.newObject.getClass().newInstance();
        } catch (InstantiationException | IllegalAccessException ex) {
        }
        Method[] metodos = q.getClass().getMethods();
        for (Method method : metodos) {
            if (method.getName().startsWith("set")) {
                try {
                    Method m = q.getClass().getMethod(method.getName().replaceFirst("set", "get"), new Class[]{});
                    Class clase = m.getReturnType();
                    if (clase == int.class || clase == double.class || clase == long.class || clase == float.class
                            || clase == Integer.class || clase == Double.class || clase == Float.class
                            || clase == Long.class) {
                        method.invoke(q, new Object[]{0});
                    }
                    if (clase == Boolean.class) {
                        method.invoke(q, new Object[]{null});
                    }
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) {
                }
            }
        }

        if (!filters.isEmpty()) {
            for (Iterator<String> it = filters.keySet().iterator(); it.hasNext();) {
                String filterProperty = it.next();
                if (filterProperty != null) {
                    String valor = filters.get(filterProperty);
                    if (filterProperty.contains(".")) {
                        tempCondicionales.add(new Condicional(filterProperty, valor));
                    } else {
                        try {
                            Method m = q.getClass().getMethod("get" + filterProperty.substring(0, 1).toUpperCase() + filterProperty.substring(1), new Class[]{});
                            Class c = m.getReturnType();
                            if (c == Boolean.class) {
                                m = q.getClass().getMethod("is" + filterProperty.substring(0, 1).toUpperCase() + filterProperty.substring(1), new Class[]{c});
                                //String valor = filters.get(filterProperty);
                                m.invoke(q, new Object[]{Boolean.valueOf(valor)});
                            } else {
                                m = q.getClass().getMethod("set" + filterProperty.substring(0, 1).toUpperCase() + filterProperty.substring(1), new Class[]{c});

                                if (c == Integer.class || c == int.class) {
                                    m.invoke(q, new Object[]{Integer.valueOf(valor)});
                                } else if (c == Long.class || c == long.class) {
                                    m.invoke(q, new Object[]{Long.valueOf(valor)});
                                } else if (c == Double.class || c == double.class) {
                                    m.invoke(q, new Object[]{Double.valueOf(valor)});
                                } else if (c == Float.class || c == float.class) {
                                    m.invoke(q, new Object[]{Float.valueOf(valor)});
                                } else if (c == String.class) {
                                    m.invoke(q, new Object[]{valor});
                                } else if (c.isEnum()) {
                                    m.invoke(q, new Object[]{Enum.valueOf(c, valor)});
                                    safe_enum = false;
                                }
                            }
                        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                        }
                    }
                }
            }
        }

        Consulta consulta = new Consulta(q);
        if (condicionales != null && !condicionales.isEmpty()) {
            for (Iterator<Condicional> it = condicionales.iterator(); it.hasNext();) {
                Condicional c = it.next();
                tempCondicionales.add(c);
            }
        }
        consulta.setCondicionales(tempCondicionales);
        consulta.SAFE_ENUM = safe_enum;
        consulta.setUnion(Consulta.TipoUnion.AND);
        consulta.setOrderBy(sortField);
        if (sortField != null) {
            if (sortOrder == SortOrder.ASCENDING) {
                consulta.setOrdenacion(Consulta.TipoOrden.ASC);
            } else {
                consulta.setOrdenacion(Consulta.TipoOrden.DESC);
            }
        }
        try {
            data = consultaUniversalFacade.consultar(consulta, new int[]{first, first + pageSize});
            int dataSize = consultaUniversalFacade.contarConsulta(consulta);
            this.setRowCount(dataSize);

        } catch (Exception e) {
        }
        if (data == null) {
            data = new ArrayList<>();
        }
        return data;
    }

    private ConsultaUniversalFacadeLocal lookupConsultaUniversalFacadeLocal() {
        try {
            Context c = new InitialContext();
            return (ConsultaUniversalFacadeLocal) c.lookup("java:global/SGR/SGR-ejb/ConsultaUniversalFacade!ejb.ConsultaUniversalFacadeLocal");
        } catch (NamingException ne) {
            Logger.getLogger(getClass().getName()).log(Level.SEVERE, "exception caught", ne);
            throw new RuntimeException(ne);
        }
    }
}
